using System;
using System.Collections.Generic;
using System.Text;
using CommonTypes.Math;
using CommonTypes.Events;
using System.ComponentModel;
using CommonTypes;
using System.Reflection;

namespace BuildingBasicDomain
{
    [PersistAtrribute (true)]
    [Serializable]
    public class Point : IPoint, ICloneable //, IPersistant
    {
        public static event EventHandler <CreationEventArgs > OnCreate;
        public event EventHandler<PersistEventArgs> OnModify;


        # region " private Decleration "
        protected Vector3D x;

        /// <summary>
        /// IPoint is not countable , But child classes Node : GridPoint  are ICountable .
        /// </summary>
        protected int id;
        public virtual int Id
        {
            get { return this.id; }
            set { this.id = value; }
        }

        # endregion

        public Point(double x, double y, double z)
            : this()
        {
            this.x = new Vector3D();
            this.x[0] = x;
            this.x[1] = y;
            this.x[2] = z;

            if (OnCreate != null)
                OnCreate(this, new CreationEventArgs());

        }
        public Point()
        {

        }

        public void RotateToZero(CommonTypes.Math.Vector3D angles)
        {
            this.RotateToPoint(new Point(0, 0, 0), angles);
        }

        public void RotateToPoint(IPoint point, CommonTypes.Math.Vector3D angles)
        {
            CommonTypes.Math.Matrix4F rotation = CommonTypes.Math.Geometry3D.Transformation.GetRotationMatrix(angles);
            CommonTypes.Math.Vector3D offset =
                new CommonTypes.Math.Vector3D(this.X - point.X, this.Y - point.Y, this.Z - point.Z);

            CommonTypes.Math.Vector4F vect4f = new CommonTypes.Math.Vector4F((float)(this.X - point.X), (float)(this.Y - point.Y), (float)(this.Z - point.Z), 1);

            CommonTypes.Math.Vector4F vect4fn = rotation * vect4f;
            CommonTypes.Math.Vector3D newVector = new CommonTypes.Math.Vector3D(vect4fn.X, vect4fn.Y, vect4fn.Z);
            newVector = newVector + point.V;
            this.X = newVector.X; this.Y = newVector.Y; this.Z = newVector.Z;
        }

        # region " IModifiable implementation "

        public bool Move(CommonTypes.Vector vector)
        {
            X = X + vector.X;
            Y = Y + vector.Y;
            Z = Z + vector.Z;



            return true;
        }

        public bool Move(double dx, double dy, double dz)
        {
            X = X + dx;
            Y = Y + dy;
            Z = Z + dz;



            return true;
        }

        #endregion

        #region " IPoint implementation "

        public double this[int i]
        {
            get
            {
                if (i < 3 && i >= 0)
                    return x[i];
                return -1;
            }
            set
            {
                if (i < 3 && i >= 0)
                    x[i] = value;
                return;
            }
        }    

        [Description("Point X"), Category("Coordinates")]
        [PersistAtrribute]
        public  double X
        {
            get { return x[0]; }
            set 
            {
                PropertyDescriptorCollection  collection  =  TypeDescriptor.GetProperties(this);
                PropertyDescriptor descriptor =  collection.Find("X", true);

                if (OnModify != null)
                    OnModify (this ,new PersistEventArgs (descriptor ,x[0],value));
                x[0] = value;
            }
        }

        [Description("Point Y"), Category("Coordinates")]
        [PersistAtrribute]
        public  double Y
        {
            get { return x[1]; }
            set 
            {
                PropertyDescriptorCollection collection = TypeDescriptor.GetProperties(this);
                PropertyDescriptor descriptor = collection.Find("Y", true);

                if (OnModify != null)
                    OnModify(this, new PersistEventArgs(descriptor, x[1], value));
               x[1] = value;
            }
        }

        [Description("Point Z"), Category("Coordinates")]
        [PersistAtrribute]
        public double Z
        {
            get { return x[2]; }
            set 
            {
                if (OnModify != null)
                {
                    PropertyDescriptorCollection collection = TypeDescriptor.GetProperties(this);
                    PropertyDescriptor descriptor = collection.Find("Z", true);

                    PersistEventArgs Args = new PersistEventArgs(descriptor, x[2], value);
                    OnModify(this,Args);
                }
                x[2] = value;
            }
        }

        public Vector3D V
        {
            set
            {
                x = value;
            }

            get
            {
                return x;
            }
        }

        public double distancePoint(IPoint point)
        {
            Vector3D diff = this.V - point.V;
            return diff.GetLength();
        }

        # endregion

        # region " Casting decleration "

        public static explicit operator Point(Vector3D point)
        {
            return new Point(point.X, point.Y, point.Z);
        }

        public static explicit operator Vector3D(Point point)
        {
            return new Vector3D(point.X, point.Y, point.Z);
        }

        public static implicit operator Vector(Point point)
        {
            return new Vector(point.X, point.Y, point.Z);
        }

        public static explicit operator Point(double[] X)
        {
            return new Point(X[0], X[1], X[2]);
        }

        public static explicit operator Point (Vector V)
        {
            return new Point(V.X ,V.Y ,V.Z);
        }

        #endregion

        # region " Ovverides decleration "

        public static bool operator ==(Point point1, Point point2)
        {
            object o1 = point1;
            object o2 = point2;

            if (o1 == null && o2 == null) return true;
            if (o1 == null) return false;
            if (o2 == null) return false;
            if (point1.GetType() != point2.GetType()) return false;

            if (point1.X == point2.X && point1.Y == point2.Y && point1.Z == point2.Z)
                return true;
            else
                return false;

        }

        public static bool operator !=(Point point1, Point point2)
        {
            return !(point1 == point2);

        }

        public override bool Equals(object obj)
        {
            if (obj is Point)
                return (bool)(this == (Point)obj);
            else return false;
        }
        /*
        public override int GetHashCode()
        {
            return (int  )(X * X  + Y*Y  + Z*Z+Id *Id);
        }
        */
        public Object Clone()
        {
            // TODO: Implement this method
            return new Point(X, Y, Z);
        }

        public override string ToString()
        {
            return this.GetType () .Name+ " ID : " + id + "X : " + X + " Y : " + Y + " Z : " + Z;
        }
        #endregion

    }


    public class PersistEventArgs: EventArgs 
    {
        string  descriptor;
        public string Descriptor
        {
            get { return descriptor; }
            set { descriptor = value; }
        }

        public PropertyDescriptor propertyDescriptor;

        object initValue;

        public object InitValue
        {
            get { return initValue; }
            set { initValue = value; }
        }
        object finalValue;

        public object FinalValue
        {
            get { return finalValue; }
            set { finalValue = value; }
        }

        public PersistEventArgs(PropertyDescriptor propertyDescriptor,
    object initvalue, object finalvalue)
        {
            this.propertyDescriptor = propertyDescriptor;
            this.InitValue = initvalue;
            this.FinalValue = finalvalue;
        }

        public PersistEventArgs(string  descriptor, 
            object initvalue,object  finalvalue)
        {
            this.descriptor = descriptor;
            this.InitValue = initvalue;
            this.FinalValue = finalvalue; 
        }

    }

    [AttributeUsage (AttributeTargets .Class | AttributeTargets .Property )]
    public class PersistAtrribute : Attribute
    {
        Type persist_type;

        public Type Persist_type
        {
            get { return persist_type; }
            set { persist_type = value; }
        }

        public PersistAtrribute()
        {
        }

        public bool checkDeep;
        public PersistAtrribute(bool checkDeep )
        {
            this.checkDeep = checkDeep;
        }

    }

    public interface IPersistant
    {
       event EventHandler<PersistEventArgs> OnModify;
    }

}
